/*********************************************************************************************************
 *  ------------------------------------------------------------------------------------------------------
 *  file description
 *  ------------------------------------------------------------------------------------------------------
 *         \file  romt.c
 *         \unit  romt
 *        \brief  This is a simple rom test module for C language
 *       \author  Lamdonn
 *      \version  v0.1.0
 *      \license  GPL-2.0
 *    \copyright  Copyright (C) 2023 Lamdonn.
 ********************************************************************************************************/
#include "romt.h"

#define PRIVATE_INDEX_RESULT_HISTORY        0
#define PRIVATE_INDEX_RESULT_LATEST         1
#define PRIVATE_INDEX_DURATION              2
#define PRIVATE_INDEX_TEST_MODE             3

/**
 * \brief Test the read functionality of the ROMT structure.
 * 
 * This function reads data from the ROMT structure in units of ROMT_UNIT_SIZE
 * until the entire ROM is read. It verifies that the number of bytes read
 * matches the expected number for each read operation.
 * 
 * \param[in] romt: Pointer to the ROMT structure to be tested.
 * \return Returns 1 if the read test is successful; otherwise, returns 0.
 */
static uint8_t test_read(ROMT *romt)
{
    uint32_t base = romt->base;               ///< Current address in the ROM
    uint32_t size = 0;                        ///< Total bytes read
    uint8_t buffer[ROMT_UNIT_SIZE];                ///< Buffer to hold read data
    uint16_t num;                             ///< Number of bytes to read in current iteration

    while (size < romt->size)                 ///< Loop until the entire ROM is read
    {
        if (romt->size - size > ROMT_UNIT_SIZE) num = ROMT_UNIT_SIZE;       ///< Determine number of bytes to read
        else num = romt->size - size;

        // Perform the read operation and check if the number of bytes read is correct
        if (num != (romt->read)(base, buffer, num))
        {
            return 0;  ///< Return 0 if read operation fails
        }

        base += num;                            ///< Update the base address
        size += num;                            ///< Update the total bytes read
    }

    return 1;                                   ///< Return 1 if the read test is successful
}

/**
 * \brief Test the write functionality of the ROMT structure.
 * 
 * This function writes data to the ROMT structure in units of ROMT_UNIT_SIZE
 * until the entire ROM is written. It verifies that the number of bytes
 * written matches the expected number for each write operation.
 * 
 * \param[in] romt: Pointer to the ROMT structure to be tested.
 * \return Returns 1 if the write test is successful; otherwise, returns 0.
 */
static uint8_t test_write(ROMT *romt)
{
    uint32_t base = romt->base;               ///< Current address in the ROM
    uint32_t size = 0;                        ///< Total bytes written
    uint8_t buffer[ROMT_UNIT_SIZE] = {0};         ///< Buffer initialized to zeros for writing
    uint16_t num;                             ///< Number of bytes to write in current iteration

    while (size < romt->size)                 ///< Loop until the entire ROM is written
    {
        if (romt->size - size > ROMT_UNIT_SIZE) num = ROMT_UNIT_SIZE;       ///< Determine number of bytes to write
        else num = romt->size - size;

        // Perform the write operation and check if the number of bytes written is correct
        if (num != (romt->write)(base, buffer, num))
        {
            return 0;  ///< Return 0 if write operation fails
        }

        base += num;                            ///< Update the base address
        size += num;                            ///< Update the total bytes written
    }

    return 1;                                   ///< Return 1 if the write test is successful
}

/**
 * \brief Test the normal functionality of the ROMT structure.
 * 
 * This function writes a pattern to the ROMT structure and then reads it back.
 * It verifies that the data read matches the pattern that was written.
 * The pattern consists of values from 0 to 255, repeating as necessary.
 * 
 * \param[in] romt: Pointer to the ROMT structure to be tested.
 * \return Returns 1 if the normal test is successful; otherwise, returns 0.
 */
static uint8_t test_normal(ROMT *romt)
{
    uint32_t base = romt->base;               ///< Current address in the ROM
    uint32_t size = 0;                        ///< Total bytes processed
    uint8_t buffer[ROMT_UNIT_SIZE];                ///< Buffer for reading and writing data
    uint16_t i, num;                         ///< Loop index and number of bytes for current iteration

    while (size < romt->size)                 ///< Loop until the entire ROM is processed
    {
        // Determine number of bytes to write in the current iteration
        if (romt->size - size > ROMT_UNIT_SIZE) 
            num = ROMT_UNIT_SIZE;                  
        else 
            num = romt->size - size;

        // Fill the buffer with a pattern (0, 1, 2, ..., 255)
        for (i = 0; i < ROMT_UNIT_SIZE; i++)
        {
            buffer[i] = (uint8_t)(i % 256);
        }

        // Perform the write operation and check if the number of bytes written is correct
        if (num != (romt->write)(base, buffer, num))
        {
            return 0;  ///< Return 0 if write operation fails
        }

        // Clear the buffer before reading
        memset(buffer, 0, sizeof(buffer));

        // Perform the read operation and check if the number of bytes read is correct
        if (num != (romt->read)(base, buffer, num))
        {
            return 0;  ///< Return 0 if read operation fails
        }

        // Verify that the read data matches the expected pattern
        for (i = 0; i < ROMT_UNIT_SIZE; i++)
        {
            if (buffer[i] != (uint8_t)(i % 256))
            {
                return 0;  ///< Return 0 if the read data does not match the expected pattern
            }
        }

        base += num;                            ///< Update the base address for the next iteration
        size += num;                            ///< Update the total bytes processed
    }

    return 1;                                   ///< Return 1 if the normal test is successful
}

/**
 * \brief Test the boundary write and read functionality of the ROMT structure.
 * 
 * This function writes data to the first and last byte of the ROMT structure
 * and then reads it back to verify that the data was written correctly.
 * 
 * \param[in] romt: Pointer to the ROMT structure to be tested.
 * \return Returns 1 if the boundary test is successful; otherwise, returns 0.
 */
static uint8_t test_boundary(ROMT *romt) 
{
    uint8_t data = 0;                        ///< Variable for data to be written and read

    // Write a value to the beginning of the ROM
    data = 0xAA;
    if ((romt->write)(romt->base, &data, 1) != 1)
    {
        return 0;  ///< Return 0 if the write operation at the start fails
    }

    // Write a value to the end of the ROM
    data = 0xBB;
    if ((romt->write)(romt->base + romt->size - 1, &data, 1) != 1)
    {
        return 0;  ///< Return 0 if the write operation at the end fails
    }

    // Read back the value from the beginning of the ROM and check if it matches
    if ((romt->read)(romt->base, &data, 1) != 1 || data != 0xAA)
    {
        return 0;  ///< Return 0 if the read operation at the start fails or if the data does not match
    }

    // Read back the value from the end of the ROM and check if it matches
    if ((romt->read)(romt->base + romt->size - 1, &data, 1) != 1 || data != 0xBB)
    {
        return 0;  ///< Return 0 if the read operation at the end fails or if the data does not match
    }

    return 1;  ///< Return 1 if the boundary write and read tests are successful
}

/**
 * \brief Test the random write and read functionality of the ROMT structure.
 * 
 * This function randomly generates addresses and data to write to the ROMT structure
 * and then reads back the data to verify that it matches what was written.
 * 
 * \param[in] romt: Pointer to the ROMT structure to be tested.
 * \param[in] iterations: Number of random write/read operations to perform.
 * \return Returns 1 if the random test is successful; otherwise, returns 0.
 */
static uint8_t test_random(ROMT *romt, uint32_t iterations) 
{
    uint8_t data, temp;                     ///< Variables for data to write and read back
    uint32_t i = 0;                         ///< Loop index
    uint32_t addr;                          ///< Random address for writing and reading

    // srand((unsigned int)time(NULL));      // Set random seed (uncomment to enable randomness)
    for (i = 0; i < iterations; i++) 
    {
        // Generate random address and data
        addr = (rand() % (romt->size)) + romt->base; // Generate a random address within the ROM size
        data = (uint8_t)(rand() % 256);              // Generate random data (0 to 255)

        // Write data to the randomly generated address
        if ((romt->write)(addr, &data, 1) != 1)
        {
            return 0;  // Return 0 if the write operation fails
        }

        // Read data back from the same address
        if ((romt->read)(addr, &temp, 1) != 1 || temp != data)
        {
            return 0;  // Return 0 if the read operation fails or if the data does not match
        }
    }

    return 1;  // Return 1 if the random write/read test succeeded
}

/**
 * \brief Test the pattern write and read functionality of the ROMT structure.
 * 
 * This function writes a specified pattern to the ROMT structure and then reads it back
 * to verify that the data matches the expected pattern.
 * 
 * \param[in] romt: Pointer to the ROMT structure to be tested.
 * \param[in] pattern: Byte value to write repeatedly to the ROM.
 * \return Returns 1 if the pattern test is successful; otherwise, returns 0.
 */
static uint8_t test_pattern(ROMT *romt, uint8_t pattern) 
{
    uint32_t base = romt->base;               ///< Current base address in the ROM
    uint32_t size = 0;                        ///< Total bytes processed
    uint8_t buffer[ROMT_UNIT_SIZE], temp[ROMT_UNIT_SIZE]; ///< Buffers for writing and reading data
    uint16_t i, num;                          ///< Loop index and number of bytes for current iteration

    // Fill the buffer with the specified pattern
    for (i = 0; i < ROMT_UNIT_SIZE; i++)
    {
        buffer[i] = pattern;
    }

    while (size < romt->size)                 ///< Loop until the entire ROM is processed
    {
        // Determine number of bytes to write in the current iteration
        if (romt->size - size > ROMT_UNIT_SIZE) 
            num = ROMT_UNIT_SIZE;                  
        else 
            num = romt->size - size;

        // Write the pattern to the ROM
        if (num != (romt->write)(base, buffer, num))
        {
            return 0;  // Return 0 if the write operation fails
        }

        memset(temp, 0, sizeof(temp));         // Clear the temporary buffer before reading

        // Read back the data from the ROM
        if (num != (romt->read)(base, temp, num))
        {
            return 0;  // Return 0 if the read operation fails
        }

        // Verify that the read data matches the expected pattern
        for (i = 0; i < ROMT_UNIT_SIZE; i++)
        {
            if (temp[i] != pattern)
            {
                return 0;  // Return 0 if the read data does not match the expected pattern
            }
        }

        base += num;                            ///< Update the base address for the next iteration
        size += num;                            ///< Update the total bytes processed
    }

    return 1;                                   ///< Return 1 if the pattern write/read test succeeded
}

int romt_init(ROMT *romt) 
{
    // Check if the provided ROMT pointer is NULL
    if (!romt) return 0;

    // Check if the size of the ROM is zero
    if (romt->size == 0) return 0;

    // Check if the read function pointer is NULL
    if (!romt->read) return 0;

    // Check if the write function pointer is NULL
    if (!romt->write) return 0;

    // Initialize the private data to zero
    memset(romt->private, 0, sizeof(romt->private));

    return 1;  // Initialization successful
}

int romt_start(ROMT *romt, uint32_t mode, uint32_t duration) 
{
    // Check if the romt pointer is valid
    if (!romt) return 0;

    // Check if the size of ROM is greater than 0
    if (romt->size == 0) return 0;

    // Check if the read function pointer is valid
    if (!romt->read) return 0;

    // Check if the write function pointer is valid
    if (!romt->write) return 0;

    // Check if mode and duration are valid
    if (mode == 0 || duration == 0) return 0;

    // Set the test mode and duration in the private data
    romt->private[PRIVATE_INDEX_TEST_MODE] = mode;
    romt->private[PRIVATE_INDEX_DURATION] = duration;

    return 1;  // Operation successful
}

int romt_stop(ROMT *romt) 
{
    // Check if the romt pointer is valid
    if (!romt) return 0;

    // Check if the size of ROM is greater than 0
    if (romt->size == 0) return 0;

    // Check if the read function pointer is valid
    if (!romt->read) return 0;

    // Check if the write function pointer is valid
    if (!romt->write) return 0;

    // Reset the test mode and duration in the private data
    romt->private[PRIVATE_INDEX_TEST_MODE] = 0;
    romt->private[PRIVATE_INDEX_DURATION] = 0;

    return 1;  // Operation successful
}

uint32_t romt_result(ROMT *romt) 
{
    // Check if the romt pointer is valid
    if (!romt) return 0xFFFFFFFF;

    // Check if the size of ROM is greater than 0
    if (romt->size == 0) return 0xFFFFFFFF;

    // Check if the read function pointer is valid
    if (!romt->read) return 0xFFFFFFFF;

    // Check if the write function pointer is valid
    if (!romt->write) return 0xFFFFFFFF;

    // Return the historical result from the private data
    return romt->private[PRIVATE_INDEX_RESULT_HISTORY];
}

uint32_t romt_result_latest(ROMT *romt) 
{
    // Check if the romt pointer is valid
    if (!romt) return 0xFFFFFFFF;

    // Check if the size of ROM is greater than 0
    if (romt->size == 0) return 0xFFFFFFFF;

    // Check if the read function pointer is valid
    if (!romt->read) return 0xFFFFFFFF;

    // Check if the write function pointer is valid
    if (!romt->write) return 0xFFFFFFFF;

    // Return the latest result from the private data
    return romt->private[PRIVATE_INDEX_RESULT_LATEST];
}

void romt_task(ROMT *romt)
{
    uint32_t result = 0;  // Variable to store the results of the tests
    uint32_t mode = 0;    // Variable to store the current testing mode

    // Check if the romt pointer is not NULL
    if (romt != NULL)
    {
        // Check if the duration is set to a special value or decrements it
        if (romt->private[PRIVATE_INDEX_DURATION] == 0xFFFFFFFF || 
            (romt->private[PRIVATE_INDEX_DURATION] > 0 && (romt->private[PRIVATE_INDEX_DURATION])--))
        {
            // Get the current testing mode
            mode = romt->private[PRIVATE_INDEX_TEST_MODE];

            // Perform read test if the read mode is enabled
            if (mode & ROMT_MODE_READ)
            {
                if (!test_read(romt))
                {
                    result |= ROMT_MODE_READ;  // Mark failure for read test
                }
            }

            // Perform write test if the write mode is enabled
            if (mode & ROMT_MODE_WRITE)
            {
                if (!test_write(romt))
                {
                    result |= ROMT_MODE_WRITE;  // Mark failure for write test
                }
            }

            // Perform normal test if the normal mode is enabled
            if (mode & ROMT_MODE_NORMAL)
            {
                if (!test_normal(romt))
                {
                    result |= ROMT_MODE_NORMAL;  // Mark failure for normal test
                }
            }

            // Perform boundary test if the boundary mode is enabled
            if (mode & ROMT_MODE_BOUNDARY)
            {
                if (!test_boundary(romt))
                {
                    result |= ROMT_MODE_BOUNDARY;  // Mark failure for boundary test
                }
            }

            // Perform random test if the random mode is enabled
            if (mode & ROMT_MODE_RANDOM)
            {
                if (!test_random(romt, 0x55))
                {
                    result |= ROMT_MODE_RANDOM;  // Mark failure for random test
                }
            }

            // Perform pattern test if the pattern mode is enabled
            if (mode & ROMT_MODE_PATTERN)
            {
                if (!test_pattern(romt, 0x55))
                {
                    result |= ROMT_MODE_PATTERN;  // Mark failure for pattern test
                }
            }

            // Update the latest result and accumulate the historical result
            romt->private[PRIVATE_INDEX_RESULT_LATEST] = result;
            romt->private[PRIVATE_INDEX_RESULT_HISTORY] |= result;
        }
    }
}